/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * moros.cc
 * Copyright (C) jurjen 2007 <jurjen@stwerff.xs4all.nl>
 *
 * moros.cc is free software.
 *
 * You may redistribute it and/or modify it under the terms of the
 * GNU General Public License, as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * moros.cc is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with main.cc.  If not, write to:
 * 	The Free Software Foundation, Inc.,
 * 	51 Franklin Street, Fifth Floor
 * 	Boston, MA  02110-1301, USA.
 */

 /** \dir src/client
   * The GTK based client for the Moros game.
   */

#define DEBUG_ROUTINE "moros"
#define DEBUG_FILE "~/.moros/log"

#include "../../config.h"
#include <libglademm/xml.h>
#include <gtkmm.h>
#include <iostream>
#include <signal.h>
#include "login.h"
#include "moros.h"
#include "question.h"
#include "line_change.h"
#include "../central/game.h"
#include "../general/log.h"
#include "../general/communicate.h"
#include "../general/arguments.h"
#include "picture.h"

#define CENTRAL_SERVER "127.0.0.1"

#ifdef ENABLE_NLS
#  include <libintl.h>
#endif

#define GLADE_FILE "/usr/lib/moros/moros.glade"
#include <sys/stat.h>
#include <sys/types.h>

void moros::config_handler(channel *ch) {
	std::string username;
	std::string password;
	while(true) {
		if (ch->key=="username") {
			username=ch->value;
			if (ch->peek("password")) {
				ch->next();
				password=ch->value;
			}
			users[username]=password;
		}
		if (!ch->next()) break;
	}
	ch->finish();
}

moros::moros() {
	user="";
	url="";
	cent_ch=NULL;
}

void moros::config() {
	config_file=getenv("HOME");
	config_file+="/.moros";
	mkdir(config_file.c_str(), S_IRWXU);
	config_file+="/config";
	comm = communicate();
    sigc::signal<void, channel*> config_handler_signal;
    config_handler_signal.connect(sigc::mem_fun(*this, &moros::config_handler));
	channel *ch=comm.reading(config_file, config_handler_signal);
	if (!ch) dbg_error("Cannot read config file");
    else comm.complete(10);
	Glib::signal_timeout().connect(sigc::mem_fun(*this, &moros::comm_poll), 200);
	Glib::signal_timeout().connect(sigc::mem_fun(*this, &moros::update_games), 20000);
	central=CENTRAL_SERVER;
}

moros::~moros() {
	map<std::string,std::string>::iterator i;
	channel *ch=comm.writing(config_file+".new");
	for(i=users.begin(); i!=users.end(); i++) {
		ch->write("username", i->first);
		ch->write("password", i->second);
	}
	ch->finish();
	rename(config_file.c_str(), (config_file+".old").c_str());
	rename((config_file+".new").c_str(), config_file.c_str());
}

/// timeout of 5 seconds to test new data on the central server or on changes from this side
bool moros::comm_poll() {
	comm.poll();
	return true;
}

long game_number=0;
long timestamp=0;

moros *game_data;

void moros::login_handler(channel *ch) {
	if (ch->key=="session") {
		comm.session=ch->value.c_str();
		ch->finish();
	}
	if (ch->key=="error") {
		login_win->message(ch->value);
		comm.session="";
		ch->finish();
	}
}

void moros::data_handler(channel *ch) {
	bool game=false;
    while(true) {
        if (ch->key=="gameid") {
            long ngame=atol(ch->value.c_str());
            games[ngame]=games[0];
            games.erase(0);
        }
        if (ch->key=="game") {
        	game=true;
            long g=load_game(ch);
            if (games[g].timestamp>timestamp) {
                timestamp=games[g].timestamp;
            }
        }
        if (ch->key=="timestamp") {
            timestamp=atol(ch->value.c_str());
        }
        if (!ch->next()) break;
    }
    if (game) game_list->load();
}

bool moros::update_games() {
    if (cent_ch != NULL) {
        cent_ch->write("timestamp", timestamp);
        cent_ch->send();
    }
    return true;
}

void moros::ip_addr(std::string host) {
    central=host;
}

void moros::main_loop(Gtk::Main kit) {
	try {
		refXml = Gnome::Glade::Xml::create(GLADE_FILE);
	} catch(const Gnome::Glade::XmlError& ex) {
	    dbg_fatal("Error opening %s: %s", GLADE_FILE, ex.what().c_str());
	}
	refXml->get_widget_derived("login", login_win);
	if (!login_win) dbg_fatal("Error opening login window");
	question *question_dial=0;
    refXml->get_widget_derived("question", question_dial);
	init_question();
	init_line_change();
	while (comm.session=="") {
		kit.run(*(login_win));
		if (user=="*") {
		    dbg_state("User selected cancel on login");
			exit(0);
		}
		if (user != "") {
			communicate comm;
            sigc::signal<void, channel*> login_handler_signal;
            login_handler_signal.connect(sigc::mem_fun(game_data, &moros::login_handler));
			channel *ch=comm.tls_client(central, AUTH_PORT, login_handler_signal);
			if (ch==0) {
				login_win->message("Could not connect");
				comm.session="";
				continue;
			}
			if (login_central) {
				ch->write("user", user);
			} else {
				ch->write("new_user", user);
			}
			ch->write("password", users[user]);
			ch->send();
			comm.complete(20);
		}
	}
	comm.user=user;
	login_central=false;
	login_chat=false;
	login_game=false;
	url="127.0.0.1"; /// \todo Ask central server for URL of central chatserver
	refXml->get_widget_derived("list_games", game_list);

    sigc::signal<void, channel*> data_handler_signal;
    data_handler_signal.connect(sigc::mem_fun(game_data, &moros::data_handler));
    cent_ch=comm.client(central, DATA_PORT, data_handler_signal);
    if (cent_ch==0) dbg_fatal("Could not connect for session");
    cent_ch->write("timestamp", timestamp);
    cent_ch->send();

	if (game_list) {
		kit.run(*(game_list));
	}
}

/// Stop the client on a normal interrupt signal

void sig_int(int i) {
    dbg_state("received a SIGINT signal, stopping the server");
    delete game_data;
    exit(1);
}

/// Main program of moros
Glib::RefPtr<Gnome::Glade::Xml> refXml;

#define arg_sig(t,a,s) {sigc::signal<void, std::string> s##_signal; \
    s##_signal.connect(sigc::mem_fun(game_data, &moros::s)); \
    arg_add(t, "", "", a, s##_signal);}

int main (int argc, char *argv[]) {
    signal(SIGINT, sig_int);
    dbg_level(DEBUG_CALLS, 0);
    unlink(DEBUG_FILE);
    dbg_init(DEBUG_FILE);
    dbg_state("Initializing");

	game_data=new moros;
	game_data->config();
	Gtk::Main kit(argc, argv);
	sigc::signal<void, std::string> dummy;
    arg_add(ARG_PROGRAM, "", "", "moros", dummy);
    arg_add(ARG_VERSION, "", "", "0.1", dummy);
    arg_add(ARG_DATE, "", "", "April 15 2007", dummy);
    arg_add(ARG_COPYRIGHT, "", "", "Copyright (C) 2007, Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>", dummy);
    arg_add(ARG_COPYRIGHT, "", "", "This is free software.  You may redistribute copies of it under the terms of", dummy);
    arg_add(ARG_COPYRIGHT, "", "", "the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.", dummy);
    arg_add(ARG_COPYRIGHT, "", "", "There is NO WARRANTY, to the extent permitted by law.", dummy);
    arg_add(ARG_USAGE, "", "", "[OPTION]...", dummy);
    arg_add(ARG_DESCR, "", "*", "GTK based client for the moros game.", dummy);
    arg_sig("C", "Central server ip address, default: 127.0.0.1", ip_addr);
    arg_extra("central", "HOST");
    arg_add(ARG_BUGS, "", "", "info@moroshq.org", dummy);
    arg_parse(argc, argv);

    add_directory("/usr/lib/moros/images");
    add_images();
    game_data->main_loop(kit);
	delete game_data;
	return 0;
}
